Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matchinfo lifetime #1225

Merged
merged 1 commit into from
Nov 19, 2023
Merged

Matchinfo lifetime #1225

merged 1 commit into from
Nov 19, 2023

Conversation

zachs18
Copy link
Contributor

@zachs18 zachs18 commented Nov 13, 2023

May fix #1223

Adds a (covariant) lifetime parameter to glib::MatchInfo, and restricts the glib::Regex methods that return MatchInfo to taking only &'input GStr as input (instead of impl IntoGStr) and returning MatchInfo<'input> that borrows from the input. Also MatchInfo::string now returns a borrowed &'input GStr instead of an owned GString.

Manually implements the MatchInfo struct and related traits since the wrapper! macro does not support lifetime generics (I used rust-analyzer's "expand macro recursively" option on the previous autogenerated impl using the wrapper! macro, copy-pasted it, and modified it as needed.)

Removes the ValueType/ToValue/FromValue impls from MatchInfo as they would be unsound (could be used to "extend" the borrowed lifetime; see glib/tests/regex_compiletest/04-no-to-value.rs). Also removes the HasParamSpec impl for MatchInfo1.

Relaxes the ToGlibPtr<'a, *mut T> for Shared<T, MM> impl's lifetime bound to only bound MM: 'a instead of MM: 'static so that MatchInfo can still use it. The methods in this trait return raw pointers, and the methods in the FromGlibPtr trait which take these pointers is unsafe, so I believe this should be fine.

Restricts the ValueType/ToValue/HasParamSpec impls to MatchInfo<'static>. FromValue<'a> is implemented for MatchInfo for all lifetimes (longer than 'a).

Manually implements the reference counting instead of using glib::shared::Shared, since otherwise caused issues with variance2.

Footnotes

  1. I removed the HasParamSpec impl for MatchInfo mostly because it "seemed" wrong; I haven't come up with a concrete example of unsoundness it could cause without FromValue/ToValue also being implemented.

  2. (Shared<_, MM> is invariant in MM, meaning holding a Shared<_, Self> in MatchInfo<'input> causes MatchInfo to be invariant in 'input)

Copy link
Member

@sdroege sdroege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks mostly good to me :)

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
pub struct MatchInfo<'input> {
inner: crate::shared::Shared<ffi::GMatchInfo, Self>,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
inner: crate::shared::Shared<ffi::GMatchInfo, Self>,
inner: std::ptr::NonNull<ffi::GMatchInfo>,

should be sufficient or not? Then the SharedMemoryManager noise below can also go away

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would work too, but would require manually implementing the reference-counting logic that Shared already implements, i.e. calling g_match_info_ref in clone instead of deriving Clone, and implementing Drop to call g_match_info_unref.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess the question then is which is simpler. Your call

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went with using NonNull and manually implementing the reference counting, since Shared also caused issues with variance (Shared<_, MM> is invariant in MM, meaning holding a Shared<_, Self> in MatchInfo<'input> causes MatchInfo to be invariant in 'input, when ideally it should be covariant).

glib/src/match_info.rs Outdated Show resolved Hide resolved
glib/src/match_info.rs Outdated Show resolved Hide resolved
@@ -527,7 +527,7 @@ impl<T, MM: SharedMemoryManager<Target = T>> Hash for Shared<T, MM> {

impl<'a, T: 'static, MM> ToGlibPtr<'a, *mut T> for Shared<T, MM>
where
MM: SharedMemoryManager<Target = T> + 'static,
MM: SharedMemoryManager<Target = T> + 'a,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably go into a separate commit (in the same PR). Is the same change needed for src/boxed.rs too, for example?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

right, we need that for gtk-rs/gtk4-rs#1280

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That bug is basically the same thing as this one here, yes

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is no longer part of this PR, since I now implement the reference counting manually instead of using SharedMemoryManager/Shared<P, MM> due to variance (#1225 (comment))

sdroege
sdroege previously approved these changes Nov 15, 2023
@zachs18
Copy link
Contributor Author

zachs18 commented Nov 17, 2023

Okay, now that MatchInfo<'input> is actually covariant in 'input I think this is done.
I also added back the ValueType/ToValue/HasParamSpec impls, but only for MatchInfo<'static>. FromValue<'a> is implemented for MatchInfo<'_> for any lifetime longer than 'a, since it's fine to convert a MatchInfo<'long> to MatchInfo<'short>. These impls allow having MatchInfo<'static> as a property (see glib/tests/regex_compiletest/06-property.rs)
Foreign code could set a MatchInfo<'static> property to a GMatchInfo that isn't actually 'static, but using foreign code is unsafe anyway, and as long as the MatchInfo isn't used after the input string, it's sound for the lifetime to "lie".

type BuilderFn = fn(&str) -> crate::ParamSpecBoxedBuilder<Self>;

fn param_spec_builder() -> Self::BuilderFn {
|name| Self::ParamSpec::builder(name)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error: redundant closure
   --> glib/src/match_info.rs:267:9
    |
267 |         |name| Self::ParamSpec::builder(name)
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `Self::ParamSpec::builder`
    |
    = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure
    = note: `-D clippy::redundant-closure` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`

Copy link
Member

@sdroege sdroege left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, looks good to me. Can you squash it all into a single commit with a meaningful commit message? Then I'll merge it :)

Implement MatchInfo manually.
Manually implements refcounting and from/into glib ptrs instead of using crate::shared::Shared,
 since using Shared<_, Self> causes the lifetime parameter to be invariant instead of covariant.
Removes array/slice conversions for `MatchInfo`.
ValueType/ToValue/HasParamSpec impls are restricted to `MatchInfo<'static>` to avoid use-after-free
 by converting `MatchInfo<'short>` to `Value`.
FromValue is implemented for MatchInfo for any lifetime, since MatchInfo is covariant anyway.
@sdroege sdroege merged commit 72b073c into gtk-rs:master Nov 19, 2023
38 of 48 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[BUG] Current (unstable) glib::Regex::match_* API is unsound (use-after-free)
3 participants